﻿using System;
using System.Threading.Tasks;
using CSRedis;
using System.Threading;

namespace RedisLock
{
    /// <summary>
    /// redis 锁
    /// </summary>
    public class LockHelper
    {
        private static CSRedisClient _redis;
        //默认过期时间10s
        private static readonly int _defaultExpires = 10;
        //等待时重试时间间隔：毫秒，默认60毫秒
        private static readonly int _retryInterval = 60;
        //1970年
        private static readonly DateTime _time1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0);

        /// <summary>
        /// 初始化Redis锁
        /// </summary>
        /// <param name="redisConnectString"></param>
        public static void Init(string redisConnectString)
        {
            _redis =new  CSRedis.CSRedisClient(redisConnectString);
            RedisHelper.Initialization(_redis);
        }

        /// <summary>
        /// 释放锁
        /// </summary>
        public static void Release(string lockKey)
        {
            _redis.Del(lockKey);
        }


        /// <summary>
        /// 获取锁
        /// </summary>
        /// <param name="key">上锁的key</param>
        /// <param name="expiresMS">过期时间：秒，默认10秒。传入的expiresSenconds如果小于等于0，为了防止死锁：那么expiresSenconds也会调整为10</param>
        /// <param name="waitTimeSenconds">等待时间，默认10s</param>
        /// <returns></returns>
        public static async Task<bool> LockAsync(string lockKey, int expiresSenconds = 10,int waitTimeSenconds = 10)
        {

            if (expiresSenconds <= 0)
            {
                expiresSenconds = _defaultExpires;
            }

            if (waitTimeSenconds <= 0)
            {
                return await _redis.SetAsync(lockKey, 1, expiresSenconds, RedisExistence.Nx);
            }

            long now = CurrentTimeStamp();
            long waitEndTime = now + waitTimeSenconds * 1000;
            long leftTime = 0;
            bool result = false;

            while(!result && now<= waitEndTime)
            {
                result = await _redis.SetAsync(lockKey, 1, expiresSenconds, RedisExistence.Nx);

                if (!result)
                {
                    leftTime = waitEndTime - now;

                    if (leftTime >= _retryInterval)
                    {
                        await Task.Delay(_retryInterval);
                    }
                    else
                    {
                        await Task.Delay((int)leftTime);
                    }

                    now = CurrentTimeStamp();
                }
            }
            
            return result;
        }

        /// <summary>
        /// 获取锁
        /// </summary>
        /// <param name="key">上锁的key</param>
        /// <param name="expiresMS">过期时间：秒，默认10秒，传入的expiresSenconds如果小于等于0，为了防止死锁：那么expiresSenconds也会调整为10</param>
        /// <param name="waitTimeSenconds">等待时间，默认10s</param>
        /// <returns></returns>
        public static bool Lock(string lockKey, int expiresSenconds = 10, int waitTimeSenconds = 10)
        {

            if (expiresSenconds <= 0)
            {
                expiresSenconds = _defaultExpires;
            }

            if (waitTimeSenconds <= 0)
            {
                return _redis.Set(lockKey, 1, expiresSenconds, RedisExistence.Nx);
            }

            long now = CurrentTimeStamp();
            long waitEndTime = now + waitTimeSenconds * 1000;
            bool result = false;

            while (!result && now <= waitEndTime)
            {
                result = _redis.Set(lockKey, 1, expiresSenconds, RedisExistence.Nx);

                if (!result)
                {
                    Thread.Sleep(_retryInterval);
                    now = CurrentTimeStamp();
                }
            }

            return result;
        }

        /// <summary>
        /// 获取时间戳
        /// </summary>
        /// <returns></returns>
        private static long CurrentTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - _time1970;
            return Convert.ToInt64(ts.TotalSeconds * 1000);
        }
    }
}
